<?php

/**
 * Fete_Application
 * @author Nguyen Duc Thuan <me@ndthuan.com>
 * @copyright Nguyen Duc Thuan <me@ndthuan.com>
 */
class Fete_Application
{
    protected $_name                        = null;
    protected $_directory                   = null;
    protected $_configsDirectoryName        = 'etc';
    protected $_controllersDirectoryName    = 'controllers';
    protected $_viewsDirectoryName          = 'views';
    protected $_configs                     = array();
    protected $_loadedControllers           = array();
    protected $_renderView                  = true;

    /**
     * @return Fete_Application
     */
    public function &setConfigsDirectoryName($name)
    {
        $this->_configsDirectoryName = $name;

        return $this;
    }

    /**
     * @return Fete_Application
     */
    public function &setControllersDirectoryName($name)
    {
        $this->_controllersDirectoryName = $name;

        return $this;
    }

    /**
     * @return Fete_Application
     */
    public function &setViewsDirectoryName($name)
    {
        $this->_viewsDirectoryName = $name;

        return $this;
    }

    /**
     *
     * @param string $filename
     * @param string $type
     * @return Fete_Application
     */
    public function &loadConfigFile($filename, $type = 'ini')
    {
        $filePath = $this->_directory . DIRECTORY_SEPARATOR
        . $this->_configsDirectoryName . DIRECTORY_SEPARATOR . $filename;

        if (!file_exists($filePath)) {
            throw new Fete_Application_Exception('Config file not found: '
                . $filename);
            return false;
        }

        if ('ini' === $type) {
            $configs = parse_ini_file($filePath, true);
        } elseif ('array' === $type) {
            $configs = require_once $filePath;
        }

        $this->_configs = array_merge($this->_configs, $configs);

        return $this;
    }

    /**
     *
     * @param string $name
     * @param string $section
     * @return mixed
     */
    public function getConfig($name, $section = null)
    {
        if (null === $section && isset($this->_configs[$name])) {
            return $this->_configs[$name];
        } elseif (isset($this->_configs[$section])
            && isset($this->_configs[$section][$name])) {
            return $this->_configs[$section][$name];
        }

        return null;
    }

    public function run($controller, $action)
    {
        if (null === $this->_directory) {
            throw new Fete_Application_Exception('Directory for '
                . $this->_name . ' is not set');
            return false;
        }

        Fete_Kernel::getInstance()->setDispatched(true);
        $controllerInstance = $this->_getControllerInstance($controller);
        $methodName = 'execute' . $this->_convertDashToCamelCase($action);

        if (!method_exists($controllerInstance, $methodName)) {
            throw new Fete_Controller_Exception('No method for ' . $action .
                ' of controller ' . $controller . ' found', 404);
            return false;
        }

        $viewsDir   = $this->_directory . DIRECTORY_SEPARATOR
            . $this->_viewsDirectoryName;
        $view       = $controllerInstance->view
            ->addHelpersDirectory($viewsDir . DIRECTORY_SEPARATOR . 'helpers');
        if (null === $view->getDirectory()) {
            $view->setDirectory($viewsDir . DIRECTORY_SEPARATOR . 'scripts');
        }
        $viewScript = $controllerInstance->$methodName();
        $controllerInstance->cleanUp();

        if (false !== $viewScript && true === $this->_renderView) {
            if (null === $viewScript) {
                $viewScript = $controller . DIRECTORY_SEPARATOR . $action;
            }

            $view->display($viewScript);
        }

        $this->_renderView = true;
    }

    protected function _convertDashToCamelCase($string)
    {
        $string = ucwords(str_replace('-', ' ', $string));
        $string = str_replace(' ', '', $string);

        return $string;
    }

    /**
     * @param $controller
     * @return Fete_Controller
     */
    protected function &_getControllerInstance($controller)
    {
        if (isset($this->_loadedControllers[$controller])) {
            return $this->_loadedControllers[$controller];
        }

        if (class_exists($controller, false)) {
            $instance = new $controller();
            $instance->setView(Fete_Kernel::getInstance()->getView())
                ->setApplication($this)
                ->setRequest(Fete_Request::getInstance())
                ->init();
            $this->_loadedControllers[$controller] = &$instance;
            return $instance;
        }

        $controllerName = $this->_convertDashToCamelCase($controller)
            . 'Controller';
        $className  = $this->_convertDashToCamelCase($this->_name)
            . '_' . $controllerName;
        $file       = $this->_directory . DIRECTORY_SEPARATOR 
            . $this->_controllersDirectoryName . DIRECTORY_SEPARATOR
            . $controllerName . '.php';

        if (file_exists($file)) {
            require_once $file;
            if (class_exists($className, false)) {
                $instance = new $className();
                $instance->setView(Fete_Kernel::getInstance()->getView())
                    ->setRequest(Fete_Request::getInstance())
                    ->setApplication($this)->init();
                $this->_loadedControllers[$controller] = &$instance;
                return $instance;
            }
        }

        throw new Fete_Application_Exception('No controllers for '
            . $controller . ' found', 404);

        return false;
    }

    /**
     *
     * @return Fete_Application
     */
    public function &stopRendering()
    {
        $this->_renderView = false;
        return $this;
    }

    /**
     *
     * @return Fete_Application
     */
    public function &startRendering()
    {
        $this->_renderView = true;
        return $this;
    }

    /**
     *
     * @param string $name
     * @return Fete_Application
     */
    public function &setName($name)
    {
        $this->_name = $name;
        return $this;
    }

    /**
     *
     * @return string
     */
    public function getName()
    {
        return $this->_name;
    }

    public function __construct($name, $directory)
    {
        $this->_name = $name;
        $this->_directory = $directory;

        set_include_path(get_include_path() . PATH_SEPARATOR .
            $directory . '/lib');

        if (file_exists($directory . '/init.php')) {
            require $directory . '/init.php';
        }
    }
}
